
const ivec2 FS_ADDR = ivec2(0.0);

const vec4 fs_stt = vec4(1.0);

uint seed = 0u;

void setSeed(uint s) {
    seed = s;
}

uint hash(uint s) {
    s ^= 2747636419u;
    s *= 2654435769u;
    s ^= s >> 16;
    s *= 2654435769u;
    s ^= s >> 16;
    s *= 2654435769u;
    return s;
}

uint randI() {
    seed = hash(seed);
    return seed;
}

float randS(uint s) {
    uint rn = hash(s);
    rn %= 1000000000u;
    float rs = float(rn);
    rs /= 1000000000.0;
    return rs;
}

float rand() {
    uint rn = randI();
    rn %= 1000000000u;
    float rs = float(rn);
    rs /= 1000000000.0;
    return rs;
}

#define setNSeed() setSeed(uint(iResolution.x*fragCoord.y+fragCoord.x + float(hash(uint(iTime*1000.0))%1000u)))




// WebGL1 需启用导数：在片元里使用 dFdx/dFdy/fwidth 做屏幕空间抗锯齿与过滤时需要此扩展。[web:69]
// #extension GL_OES_standard_derivatives : enable // 在 WebGL1 下开启导数函数支持，WebGL2 已内建无需此行。[web:69]

// uniform float uTime;                 // 动画时间（秒），用于控制相位推进与流动方向。[web:100]
uniform float uOpacity;              // 全局透明度系数，用于统一控制片元 Alpha。[web:8]
uniform vec3  uColorA, uColorB;      // 基础颜色 A 与 B，用于后续颜色插值混合。[web:43]
// varying vec2  vUv;                   // 从顶点插值到片元的 UV 坐标，范围通常为 [0,1]。[web:8]
varying float uRandom;               // 外部提供的随机种子（可按实例/物体变化）以产生去同质化效果。[web:124]

#define S smoothstep                 // 宏别名：S(a,b,x) 等价于 smoothstep(a,b,x) 的软阈值插值。[web:2]
const float PI = 3.14159265;         // 圆周率常量，用于正弦/余弦等周期函数计算。[web:43]

// 简单 hash（段/线道随机）
// 常见的一行哈希：通过 fract(sin(x)*常数) 生成 0..1 的确定性伪随机数。[web:92]
float hash11(float x){ return fract(sin(x)*43758.5453123); }

// 软边脉冲窗：t∈[0,1)
// 用两次 smoothstep 形成占空比为 duty、边缘宽度为 edge 的“开窗”函数，避免硬边锯齿。[web:2]
float pulse01(float t, float duty, float edge){
  float a = S(0.0, edge, t);         // 在 [0,edge] 内平滑由 0 → 1，形成软起始边。[web:2]
  float b = S(duty, duty+edge, t);   // 在 [duty,duty+edge] 内平滑由 0 → 1，用于计算软结束边。[web:2]
  return clamp(a*(1.0-b), 0.0, 1.0); // a 与 (1-b) 相乘得到中间平顶段，并夹到 0..1 保持稳定。[web:2]
//   return clamp((b-a), 0.0, 1.0); // a 与 (1-b) 相乘得到中间平顶段，并夹到 0..1 保持稳定。[web:2]
}

void main(){
    vec2 vUv = gl_FragCoord.xy / iResolution.xy;
  // 归一化到 [-1,1]：把 UV 从 [0,1] 映射到中心在原点的坐标，便于围绕 y=0 做对称造型。[web:100]
    vec2 st = vUv*2.0 - 1.0;

  // 线道配置：设定纵向流线的条数与横向分布范围。[web:100]
  const float LANES   = 12.0;        // 线道数量为 12 条，可按需求增减密度。[web:100]
  float margin  = 0.08;              // 左右留白比例，避免贴边过近产生裁切或变形感。[web:100]
  float span    = 2.0 - 2.0*margin;  // 可用横向总宽度（[-1,1] 去掉左右边距）。[web:100]
  float spacing = span / LANES;      // 邻近线道间距，随 LANES 自动适配。[web:100]

  // 纵向虚线参数（向后流动: 负相位）
  float repeat  = 1.0;              // 沿 y 的周期重复次数，控制每屏可见的虚线段数。[web:63]
  float duty    = 0.75;              // 占空比，控制每个周期内“点亮”部分的比例。[web:2]
  float edge    = 0.03;              // 虚线段两端软边宽度，避免锯齿与闪烁。[web:2]
  float speed   = 0.7;               // 流动速度的尺度系数，绝对值越大移动越快。[web:100]
  float phaseT  = -speed * iTime;    // 负相位让相位随时间减少，视觉上沿 -y 方向“向后”运动。[web:63]

  // 线宽：中间粗两头细（Hann 窗）
  float s     = clamp(1.0 - abs(st.y), 0.0, 1.0);         // 把 |y| 转成中心为 1、两端趋 0 的权重曲线。[web:100]
  float bell  = 0.5 - 0.5*cos(PI * s);                    // 提升余弦（Hann/raised-cosine）窗，平滑钟形峰。[web:86]
  float wMid  = 0.005;                                    // 中部半宽（线在中间最粗的目标宽度）。[web:86]
  float wEnd  = 0.005;                                    // 端部半宽（两端渐细的目标宽度）。[web:86]
  float pw    = 1.2;                                      // 形状指数，增大使钟形更尖锐、更偏向中部。[web:86]
  float baseW = mix(wEnd, wMid, pow(bell, pw));           // 根据钟形权重混合端宽与中宽，得到随 y 变化的半宽。[web:43]

  // 轻微横摆：通过小幅正弦扰动让线条略微左右摇曳，避免生硬。[web:43]
  float wobAmp = 0.019;            // 摆幅（单位为 st 坐标），保持小于数倍半宽以免变成波带。[web:100]
  float wobKY  = 8.0;              // 沿 y 的摆动空间频率，数值越大抖动越密。[web:43]
  float wobWT  = 1.8;              // 时间角频，控制摆动快慢。[web:43]

  // 颜色与强度：先混合两色得到基色，后续以强度乘法形成发光感。[web:43]
  vec3  baseCol = vec3( 1.0 ); // 颜色线性插值，权重此处为 0.5（等权混色）。[web:43]
  float intensity = 0.0;                         // 汇总每条线的亮度贡献以便叠加。[web:100]

  for(float i=0.0;i<LANES;i+=1.0){               // 遍历每条线道并逐条累计强度。[web:100]
    float u  = (i+0.5)/LANES;                    // 归一化索引放到 (0,1) 中心，避免贴边。[web:100]
    // x range is [-1,1];  start point = -1.0 + margin; 
    // Then every dashline(虚线线道 total quantities: LANES) starts from -1.0 + margin + u * span
    // u is the factor of one lane; span is the total width of all lanes.
    // so xi is the middle X position of one lane.
    float xi = -1.0 + margin + u*span;           // 计算该线道的横向中心位置（含左右留白）。[web:100]
    // float jit= (hash11(i+uRandom*17.0)-0.5)*spacing*0.12; // 以哈希生成细微抖动，打散规律感。[web:92]
    float jit= (hash11(i+rand()*17.0)-0.5)*spacing*0.12; // 以哈希生成细微抖动，打散规律感。[web:92]
    xi += jit;                                   // 应用抖动得到更自然的分布。[web:92]

    float phi = 6.2831*hash11(i+3.7);            // 为每条线生成独立的随机相位（2π≈6.2831）。[web:92]
    xi += wobAmp * sin(wobKY*st.y + wobWT*iTime + phi); // 叠加轻微横摆，随 y 与时间变化。[web:43]

    // SDF 即创建多条虚线的核心方法 --> Xi 随时间扭动, 求这一帧中 当前着色器处理的片元与当前循环中扭动的线的距离 d
    // Now the shader is dealing with one fragment and we need to calculate the SDF value;
    float d     = abs(st.x - xi);                // 片元到中心线的横向距离，作为距离场核的自变量。[web:104]
    //  传入一个变量 根据屏幕分辨率获得导数delta 这个Delta 在smoothstep 中作为边缘渐变宽度
    float delta = fwidth(d);                     // 屏幕空间导数宽度，用于构造与分辨率无关的软边界。[web:146]

    // Assigning this value by yourself is ok. It's no need to consider about UV position.
    float w = baseW;                             // 当前 y 上的线半宽，来自 Hann 窗调制结果。[web:86]

    // chage the value you will know what it means;
    // 设计的时候先不考虑iTime 先想好一帧画面该怎么绘制 GPU绘制时 每一个片元都并行处理 也就是每一个像素点都同时画
    float core = 1.0 - S(w - delta, w + delta, d);                  // 核心细线：以 ±delta 软化边缘抑制锯齿。[web:2]
    float halo = 1.0 - S(3.0*w - 2.0*delta, 3.0*w + 2.0*delta, d);  // 外圈光晕：更宽阈值制造柔和发光边。[web:2]

    // 给虚线增加随机扰动 -->  主要就是这个 segSeed; 
    float segId   = floor(st.y*repeat + phaseT + hash11(i+7.0));    // 计算所在的纵向段 ID，用于稳定的段内随机。[web:63]
    float segSeed = hash11(segId + i*121.0);                         // 基于段 ID 与线道索引的伪随机种子。[web:92]
    
    float t       = fract(st.y*repeat + phaseT + segSeed);          // 周期相位 t∈[0,1)，随时间负向推进即向后流动。[web:63] 用于创建Dash时的相位
    float dutyJit = clamp(duty + (segSeed-0.5)*0.18, 0.15, 0.85);   // 每段占空比加随机扰动并夹取安全范围。[web:2]  占空比添加了随机扰动(segSeed-0.5)*0.18
    /*
        fract 如此运作: st.y 从 0.0 ~ 1.0 变化, 在当前这条线上(for循环的一个i中), 会有多少次重复, 
        比如 repeat = 1;  float t = fract(st.y*repeat); 时, t 中没有随机变化 在显示的画面中就只会出现 在一条line上只有两段, 因为早已经将UV范围变为了[-1, 1], 取小数部分能取到两次;
        fract( st.y * 3 ) [ y∈[0,1], 则沿着Y轴会被切分成3段 ]
        但是加上时间和segSeed随机扰动时 就会变得比较凌乱
    */

    // 创建虚线
    float dash    = pulse01(t, dutyJit, edge);                      // 生成软边虚线窗，避免硬切导致闪烁。[web:2]

    // float lane = (core*1.0 + halo*0.35) * dash * (0.6 + 0.7*segSeed); // 核+光晕乘上虚线窗并以随机亮度缩放。[web:2]
    float lane = (core*1.0) * dash * (0.6 + 0.7*segSeed); // 核 乘上虚线窗并以随机亮度缩放。[web:2]
    intensity += lane;                                              // 把该线道贡献累加进总强度。[web:100]
    // intensity += core;                                              // 把该线道贡献累加进总强度。[web:100]
  }

  float glow = intensity;                 // 将累积强度作为“发光”系数，后续可接入 Bloom 增强表现。[web:81]
  vec3  col  = baseCol * glow;            // 以强度调制颜色，形成加亮的细线效果。[web:43]

  float fadeL = S(0.00, 0.08, vUv.x);     // 左侧淡入：遵循 edge0<edge1 的安全顺序避免未定义行为。[web:2][web:3]
  float fadeR = 1.0 - S(0.92, 1.00, vUv.x); // 右侧淡出：用 1- smoothstep 的等价安全写法。[web:2][web:3]
  float alpha = uOpacity * fadeL * fadeR * clamp(glow, 0.0, 1.0); // 组合透明度并限制到 [0,1]，避免过曝溢出。[web:8]

  gl_FragColor = vec4(col, alpha);        // 输出最终片元颜色与透明度（WebGL1/GLSL ES 100 接口）。[web:8]
}
